package com.clei.netty.reconnect.client;

import com.clei.consts.NettyConstants;
import com.clei.utils.PrintUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.util.Scanner;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * reconnect server
 *
 * @author KIyA
 */
public class ReconnectClient {

    /**
     * 连接锁
     */
    private static final Object CONNECT_LOCK = new Object();
    /**
     * 任务执行器
     */
    private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
    /**
     * channel
     */
    private volatile Channel channel = null;
    /**
     * 连接中
     */
    private volatile boolean connecting = false;

    public static void main(String[] args) {
        ReconnectClient client = new ReconnectClient();
        client.checkConnection();
        client.connect();
    }

    public void connect() {
        synchronized (CONNECT_LOCK) {
            if (connecting) {
                return;
            }
            connecting = true;
        }

        PrintUtil.log("Reconnect Client 启动");
        // 启动
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(bossGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ReconnectClientInitializer());
            // 连接
            Channel c = bootstrap.connect(NettyConstants.SERVER_IP, NettyConstants.SERVER_PORT).sync().channel();
            this.channel = c;
            connecting = false;
            PrintUtil.log("Reconnect Client 连接成功");
            // 这里没输入的话多次断连可能会占满线程池
            while (c.isWritable()) {
                Scanner scanner = new Scanner(System.in);
                String msg = scanner.nextLine();
                c.writeAndFlush(msg + "\n");
            }
            // 阻塞
            // channel.closeFuture().sync();
            PrintUtil.log("Reconnect Client 连接断开");
        } catch (Exception e) {
            PrintUtil.log(e);
        } finally {
            bossGroup.shutdownGracefully();
        }
    }

    /**
     * 检查连接
     */
    private void checkConnection() {
        Runnable r = () -> {
            PrintUtil.log("checkConnection...");
            // 距离上次请求时间大于等于1分钟 则发送链路保持请求消息
            Channel c = this.getChannel();
            boolean active = c.isActive();
            PrintUtil.log("channel : {}, isActive : {}", c, active);
            if (!active) {
                executor.execute(this::connect);
                PrintUtil.log("checkConnection 重连");
            }
        };
        executor.scheduleAtFixedRate(r, 15, 15, TimeUnit.SECONDS);
    }

    public Channel getChannel() {
        return channel;
    }
}
